package es.us.idinfor.rmplugin.operator;

import java.util.List;

import Jama.Matrix;

import com.rapidminer.example.Attribute;
import com.rapidminer.example.ExampleSet;

import com.rapidminer.operator.OperatorDescription;
import com.rapidminer.operator.OperatorException;
import com.rapidminer.operator.ValueDouble;
import com.rapidminer.operator.ValueString;
import com.rapidminer.operator.features.selection.AbstractFeatureSelection;

import com.rapidminer.parameter.ParameterType;
import com.rapidminer.parameter.ParameterTypeBoolean;
import com.rapidminer.parameter.ParameterTypeCategory;
import com.rapidminer.parameter.ParameterTypeDouble;
import com.rapidminer.parameter.conditions.BooleanParameterCondition;

import es.us.idinfor.rmplugin.algorithm.AmevaAlgorithm;
import es.us.idinfor.rmplugin.discretizer.AmevaDiscretizer;
import es.us.idinfor.rmplugin.example.ExamplesFactory;
import es.us.idinfor.rmplugin.util.Utils;

/**
 * <p>This operator removes dependences features from a threshold parameter.<br />
 * It uses the Ameva coefficient to get a value of dependence between features.</p>
 * 
 * <p>This operator needs a class feature to calculate the Ameva coefficient.</p>
 * 
 * @author Miguel Angel Alvarez de la Concepcion
 * @version 1.0
 */
public class AmevaSelection extends AbstractFeatureSelection {
	
	/**
	 * The parameter name for the outlier percetage.
	 */
	public static final String PARAMETER_OUTLIER_PERCENTAGE = "outlier_percentage";
	
	/**
	 * The parameter name for the Ameva approximation method.
	 */
	public static final String PARAMETER_APPROXIMATION = "Ameva_approximation_method";
	private static final String[] APPROXIMATION_TYPES = new String[] {"default", "equals", "double"};
	
	/**
	 * The parameter name for the threshold value.
	 */
	public static final String PARAMETER_THRESHOLD = "threshold";
	
	/**
	 * The parameter name for the G-Matrix.
	 */
	public static final String PARAMETER_G_MATRIX = "G-Matrix";
	
	/**
	 * The parameter name for the typification.
	 */
	public static final String PARAMETER_TYPIFY = "typify";
	
	/**
	 * The parameter name for the discretization.
	 */
	public static final String PARAMETER_DISCRETIZE = "discretize";
	
	private String features;
	private double numFeatures;
	
	/**
	 * Default constructor.
	 * 
	 * @param description The description of the operator.
	 */
	public AmevaSelection(OperatorDescription description) {
		super(description);
		
		features = "";
		numFeatures = 0.0;
		
		addValue(new ValueString("features", "The selected features of the returned ExampleSet.", false) {
			public String getStringValue() {
				return features;
			}
		});
		
		addValue(new ValueDouble("num_features", "The number of selected features of the returned ExampleSet.", false) {
			public double getDoubleValue() {
				return numFeatures;
			}
		});
	}
	
	/**
	 * @see Operator#getParameterTypes()
	 */
	public List<ParameterType> getParameterTypes() {
		List<ParameterType> types = super.getParameterTypes();
		ParameterType type = null;
		
		type = new ParameterTypeDouble(PARAMETER_THRESHOLD, "The threshold value for consider if two features are dependents.", 0.0d, 1.0d, 0.8d, false);
		types.add(type);
		
		type = new ParameterTypeCategory(PARAMETER_APPROXIMATION, "The approximation of the Ameva coefficient.", APPROXIMATION_TYPES, 0, true);
		types.add(type);
		
		type = new ParameterTypeBoolean(PARAMETER_TYPIFY, "Check it if you want to typify the dataset values.", true, true);
		types.add(type);
		
		type = new ParameterTypeBoolean(PARAMETER_DISCRETIZE, "Check it if you want to discretize the dataset values.", true, true);
		types.add(type);
		
		type = new ParameterTypeDouble(PARAMETER_OUTLIER_PERCENTAGE, "The percentage of values that they are considered outlier.", 0.0d, 100.0d, 10.0d, false);
		type.registerDependencyCondition(new BooleanParameterCondition(this, PARAMETER_DISCRETIZE, true, true));
		types.add(type);
		
		type = new ParameterTypeBoolean(PARAMETER_G_MATRIX, "Check it if you only want to use the G-Matriz instead of G*-Matriz.", false, true);
		types.add(type);
		
		type = null;
		
		return types;
	}
	
	/**
	 * @see AbstractExampleSetProcessing#apply(ExampleSet)
	 */
	public ExampleSet apply(ExampleSet in) throws OperatorException {
		if(in.getAttributes().getLabel() == null) {
			logError("There is not a label attribute.");
			throw new OperatorException("Ameva Selection operator must receive an example set with a label.");
		}
		else if(in.getAttributes().getLabel().isNumerical()) {
			logError("The label attribute is not nominal.");
			throw new OperatorException("Ameva Selection operator must receive an example set with a nominal label.");
		}
		else {
			try {
				Matrix m = AmevaAlgorithm.execute(in, getParameterAsString(PARAMETER_APPROXIMATION), getParameterAsDouble(PARAMETER_THRESHOLD), !getParameterAsBoolean(PARAMETER_G_MATRIX));
				ExampleSet out = ExamplesFactory.getCopy(in, m);
				AmevaDiscretizer ameva = null;
				
				if(getParameterAsBoolean(PARAMETER_TYPIFY)) {
					Utils.typify(out);
				}
				
				if(getParameterAsBoolean(PARAMETER_DISCRETIZE)) {
					ameva = new AmevaDiscretizer(out);
					ameva.discretize(out.getAttributes(), false, getParameterAsDouble(PARAMETER_OUTLIER_PERCENTAGE));
				}
				
				features = "";
				
				for(Attribute att: out.getAttributes()) {
					features += "," + att.getName();
				}
				
				features = features.substring(1);
				numFeatures = out.getAttributes().size();
				
				ameva = null;
				m = null;
				
				return out;
			} catch(OperatorException e) {
				throw new OperatorException("Ameva Selection operator has had an error with a copy.");
			}
		}
	}
}
